from abc import ABC, abstractmethod
from datetime import datetime
import pandas as pd
from importlib import resources
from typing import List


class Position:
    def __init__(self, order_book_id: str, volume: int, market_value: float):
        self.order_book_id = order_book_id
        self.volume = volume
        self.market_value = market_value


class Order:
    def __init__(self, order_book_id: str, volume: int, price: float):
        self.order_book_id = order_book_id
        self.volume = volume
        self.price = price


class Trader(ABC):
    def __init__(self):
        pass

    @abstractmethod
    def is_suspended(self, order_book_id: str, dt: datetime):
        pass

    @abstractmethod
    def order_target_percent(self,
                             context,
                             order_book_id: str,
                             target_percent: float,
                             price: float = None):
        pass

    @abstractmethod
    def order_target_value(self,
                           context,
                           order_book_id: str,
                           target_value: float,
                           price: float = None):
        pass

    @abstractmethod
    def order_target_volume(self,
                            context,
                            order_book_id: str,
                            target_volume: int,
                            price: float = None):
        pass

    @abstractmethod
    def all_instruments(self, ins_type: str, dt: str):
        pass

    @abstractmethod
    def get_next_trading_date(self, datestr: str, n: int):
        pass

    @abstractmethod
    def run_weekly(self, handle_bar_fn, tradingday: int):
        pass

    @abstractmethod
    def run_daily(self, handle_bar_fn, at: str = None):
        pass

    @abstractmethod
    def run_monthly(self, handle_bar_fn, tradingday: int):
        pass

    @abstractmethod
    def cash(self, context):
        pass

    @abstractmethod
    def nav(self, context):
        pass

    @abstractmethod
    def subscribe(self, order_book_ids, freq):
        pass

    @abstractmethod
    def get_positions(self, context) -> List:
        pass

    @abstractmethod
    def get_position(self, context, order_book_id: str):
        pass

    @staticmethod
    def trading_date_in_n(datestr: str, n: int):
        with resources.path(__package__, 'trading_dates.csv') as f:
            td = pd.read_csv(f).trading_date.tolist()
        try:
            idx = td.index(datestr)
            return td[idx + n]
        except ValueError:
            import bisect
            if n >= 0:
                idx = bisect.bisect(td, datestr) - 1
                return td[idx + n]
            else:
                idx = bisect.bisect_right(td, datestr)
                return td[idx + n]
